﻿#region Usings
using System;
using System.Threading;
using System.Collections.Generic;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
#endregion

namespace ghrb.Managers
{
    //Handles The input (thread)
    public class InputManager
    {
        #region Public Functions
        /// <summary>
        /// Input manager constructor
        /// </summary>
        /// <param name="index">Index of the player</param>
        public InputManager(PlayerIndex index)
        {
            //Set Player index
            m_PlayerIndex = index;

            //Get the current state
            m_gpOldState = m_gpState = GamePad.GetState(m_PlayerIndex);

            //Set Thread
            ThreadStart threadStart = new ThreadStart(CheckInput);
            m_ThreadInputCheck = new Thread(threadStart);
        }

        /// <summary>
        /// Update the input manager
        /// </summary>
        /// <param name="gameTime">Game time</param>
        public void Update(GameTime gameTime)
        {
            //Set Old State
            m_gpOldState = m_gpState;

            //Get the current state
            m_gpState = GamePad.GetState(m_PlayerIndex);

            //Add passed time
            m_TimeSpan += gameTime.ElapsedGameTime;

            if (m_PlayerIndex == PlayerIndex.Two && !m_bPlay)
                return;
        }

        /// <summary>
        /// Check if a key is down
        /// </summary>
        /// <param name="k">Key</param>
        public bool IsKeyDown(Key k)
        {
            //If current state is down
            return IsKeyDown(k, false);
        }

        /// <summary>
        /// Check if a key is released
        /// </summary>
        /// <param name="k">Key</param>
        public bool KeyRelease(Key k)
        {
            //If old state is down but not current
            return (IsKeyDown(k, true) && !IsKeyDown(k, false));
        }

        /// <summary>
        /// Check if a key is pressed (only triggers on press)
        /// </summary>
        /// <param name="k">Key</param>
        public bool KeyDownOnce(Key k)
        {
            //If current state is down but old not
            return (IsKeyDown(k, false) && !IsKeyDown(k, true));
        }

        /// <summary>
        /// Function to check input (multi thread this one)
        /// </summary>
        public void CheckInput()
        {
            //While the player is playing
            while (m_bPlay)
            {
                //If the controller is connected (sometimes lose its connection)
                if (m_gpState.IsConnected)
                {
                    //Create pressed state
                    KeyInput pressedKey;
                    pressedKey.KeyState = KeyState.Down;
                    pressedKey.TimeStamp = m_TimeSpan;

                    //Create released state
                    KeyInput releasedKey;
                    releasedKey.KeyState = KeyState.Up;
                    releasedKey.TimeStamp = m_TimeSpan;

                    //Check if green is released or pressed
                    if (KeyDownOnce(Key.GREEN))
                        m_KeyInputsGreen.Enqueue(pressedKey);
                    else if (KeyRelease(Key.GREEN))
                        m_KeyInputsGreen.Enqueue(releasedKey);

                    //Check if red is released or pressed
                    if (KeyDownOnce(Key.RED))
                        m_KeyInputsRed.Enqueue(pressedKey);
                    else if (KeyRelease(Key.RED))
                        m_KeyInputsRed.Enqueue(releasedKey);

                    //Check if yellow is released or pressed
                    if (KeyDownOnce(Key.YELLOW))
                        m_KeyInputsYellow.Enqueue(pressedKey);
                    else if (KeyRelease(Key.YELLOW))
                        m_KeyInputsYellow.Enqueue(releasedKey);

                    //Check if blue is released or pressed
                    if (KeyDownOnce(Key.BLUE))
                        m_KeyInputsBlue.Enqueue(pressedKey);
                    else if (KeyRelease(Key.BLUE))
                        m_KeyInputsBlue.Enqueue(releasedKey);

                    //Check if orange is released or pressed
                    if (KeyDownOnce(Key.ORANGE))
                        m_KeyInputsOrange.Enqueue(pressedKey);
                    else if (KeyRelease(Key.ORANGE))
                        m_KeyInputsOrange.Enqueue(releasedKey);
                }

                //Sleep the thread
                Thread.Sleep(15);
            }
        }

        /// <summary>
        /// Reset input data
        /// </summary>
        public void Reset()
        {
            //Clear all button queues
            m_KeyInputsGreen.Clear();
            m_KeyInputsRed.Clear();
            m_KeyInputsYellow.Clear();
            m_KeyInputsBlue.Clear();
            m_KeyInputsOrange.Clear();

            //Reset time span
            m_TimeSpan -= m_TimeSpan;
        }

        /// <summary>
        /// Start the input listener thread
        /// </summary>
        public void StartThread()
        {
            m_ThreadInputCheck.Start();
        }
        #endregion

        #region Private Functions
        private bool IsKeyDown(Key k, bool previousState)
        {
            //Get controller state
            GamePadState gpState;

            //Set old or current state
            if (previousState)
                gpState = m_gpOldState;
            else
                gpState = m_gpState;

            //If controller is connected
            if (gpState.IsConnected)
            {
                switch (k)
                {
                    //Green button
                    case Key.GREEN:
                        return (gpState.Buttons.A == ButtonState.Pressed) ? true : false;

                    //Red button
                    case Key.RED:
                        return (gpState.Buttons.B == ButtonState.Pressed) ? true : false;

                    //Yellow button
                    case Key.YELLOW:
                        return (gpState.Buttons.Y == ButtonState.Pressed) ? true : false;

                    //Blue button
                    case Key.BLUE:
                        return (gpState.Buttons.X == ButtonState.Pressed) ? true : false;

                    //Orange button
                    case Key.ORANGE:
                        return (gpState.Buttons.LeftShoulder == ButtonState.Pressed) ? true : false;

                /*
                * Whammy = ThumbRX -32768 = off, 32768 = complete on
                */
                }
            }

            //return false by default
            return false;
        }
        #endregion

        #region Properties and Vars
        /// <summary>
        /// Player index
        /// </summary>
        private PlayerIndex m_PlayerIndex = PlayerIndex.One;

        /// <summary>
        /// Old GamePadState
        /// </summary>
        private GamePadState m_gpOldState;

        /// <summary>
        /// Current GamePadState
        /// </summary>
        private GamePadState m_gpState;

        /// <summary>
        /// Controller connected
        /// </summary>
        public bool IsConnected
        {
            get { return m_gpState.IsConnected; }
        }

        /// <summary>
        /// If the player is playing
        /// </summary>
        private bool m_bPlay = true;

        /// <summary>
        /// The Queue 
        /// </summary>
        private Queue<KeyInput> m_KeyInputsGreen = new Queue<KeyInput>();
        private Queue<KeyInput> m_KeyInputsRed = new Queue<KeyInput>();
        private Queue<KeyInput> m_KeyInputsYellow = new Queue<KeyInput>();
        private Queue<KeyInput> m_KeyInputsBlue = new Queue<KeyInput>();
        private Queue<KeyInput> m_KeyInputsOrange = new Queue<KeyInput>();

        /// <summary>
        /// The time span of the song
        /// </summary>
        private TimeSpan m_TimeSpan = new TimeSpan();

        private Thread m_ThreadInputCheck;
        #endregion
    }
}
